#!/usr/bin/python
# -*- coding:utf-8 -*-

"""
对外API 资产管理
errcode = 10
"""
import arrow
import copy
import bson
import re

from bson import ObjectId
from flask import g
from pydantic.error_wrappers import ValidationError

from app.api.v2.asset_util import update_asset, del_asset, add_asset, add_host_asset
from app.api.v3 import asset_util
from app.api.v3.asset_util import check_add_asset_params
from app.db.models.jobs import Job
from app.libs.error_code import ERROR_CODE_MAP
from app.libs.redprint import RedPrint
from app.libs.regexp import NAME_RE
from app.libs.utility import api_v3_response, get_request_args_v3
from app.errors import APIException, ParameterError
from app.validators.asset import BatchAssetJobId
from app.log.logs import CJobLog


api = RedPrint('assets')


@api.route('/asset-list', methods=['GET'])
def asset_list():
    """资产列表"""
    resp_fields = ['id', 'note', 'sourceIp', 'targetUrl', 'alertSettings', 'assetType', 'createTime']

    query = get_request_args_v3(["page", "limit", "target_id", "target", "target_name",
                                 "target_type", "created_start_at", "created_end_at"])
    q = {'uid': g.audit_uid}

    if query.get('target_type') is not None:
        if query.get('target_type') == 'host':
            q['assetType'] = "host"
        else:
            q['assetType'] = {'$ne': 'host'}

    if query.get('target') is not None:
        q['targetUrl'] = {'$regex': re.escape(query['target'])}
    if query.get('target_name') is not None:
        q['note'] = {'$regex': re.escape(query['target_name'])}
    if query.get("target_id") is not None:
        try:
            q["_id"] = bson.ObjectId(re.escape(query.get("target_id")))
        except bson.errors.InvalidId:
            return api_v3_response(status="fail", errcode=1040001, data=[])
    page, limit = query.get("page", 1),  query.get("limit", 10)
    start_at = query.get("created_start_at")
    end_at = query.get("created_end_at")
    if start_at:
        try:
            start_at = arrow.get(start_at).datetime
        except arrow.parser.ParserError:
            return api_v3_response(status="fail", errcode=1040021, data={})
    if end_at:
        try:
            end_at = arrow.get(end_at).datetime
        except arrow.parser.ParserError:
            return api_v3_response(status="fail", errcode=1040021, data={})
    if start_at:
        q.setdefault('createTime', {})['$gte'] = start_at
    if end_at:
        q.setdefault('createTime', {})['$lte'] = end_at
    sort = ['-id']

    try:
        jobs, count = asset_util.query_job_list(q, resp_fields, page, limit, sort)
    except APIException:
        return api_v3_response(status="fail", errcode=1040002, data=[])

    results = asset_util.deal_job_info(jobs)
    data = {"page": page, "limit": limit, "count": count, "results": results}
    return api_v3_response(data=data)


@api.route('/asset-add', methods=['POST'])
def asset_add():
    """添加资产"""
    request_args = get_request_args_v3(["target_list", ])
    fail_count = 0
    success_count = 0
    fail_list = []
    success_list = []
    uid, username = g.audit_uid, g.user.username
    err_data_tmp = {"target": "", "target_name": "", "target_ip": "", "target_type": "", "info": ""}

    target_list = request_args.get("target_list", [])
    if not target_list:
        return api_v3_response(status="fail", errcode=1040003, data={"fail": [], "success": []})
    if len(target_list) > 50:
        return api_v3_response(status="fail", errcode=1040004, data={"fail": [], "success": []})

    for target in target_list:
        if not isinstance(target, dict):
            fail_count += 1
            err_data = copy.copy(err_data_tmp)
            err_data["info"] = f"{target} 不是一条有效的数据"
            fail_list.append(err_data)
            continue

        status, params = check_add_asset_params(target)
        if not status:
            target["info"] = params
            fail_list.append(target)
            continue

        try:
            target_type = params.get("target_type")
            if target_type == "web":
                args_data = {'note': params.get("note"), 'sourceIp': params.get("source_ip"),
                             'url': params.get("target_url")}
                status, job = add_asset(args_data, uid, username)
            else:
                temp_data = {'note': params.get("note"), 'sourceIp': params.get("target_url"), 'assetType': target_type}
                status, job = add_host_asset(temp_data, uid, username)

            if not status:
                target["info"] = job
                fail_list.append(target)
                continue

        except ParameterError as e:
            target["info"] = e.msg
            fail_list.append(target)
            continue
        except ValidationError:
            target["info"] = "请检查参数"
            fail_list.append(target)
            continue
        except APIException as e:
            target["info"] = e.msg
            fail_list.append(target)
            continue
        else:
            success_count += 1
            target["target_id"] = job.id
            target["info"] = "添加成功"
            success_list.append(target)

    success_url = [{j.get("target_name"): j.get("target")} for j in success_list]
    fail_url = [{j.get("target_name"): j.get("target")} for j in fail_list]

    fail_count = len(fail_list)
    success_count = len(success_list)

    log_info = {
        'affects': [j.get("target") for j in success_list],
        'actionMessage': 'API新增资产',
        'detail': f'API新增资产，成功{success_count}个: {success_url}, 失败{fail_count}个: {fail_url}'
    }

    CJobLog(**log_info).info(log_info['actionMessage'], log_info['detail'])
    return api_v3_response(message=f"增加资产，成功{success_count}个, 失败{fail_count}个",
                           data={"fail": fail_list, "success": success_list})


@api.route('/asset-update', methods=['POST'])
def asset_update():
    """资产编辑 只允许编辑 note"""
    args = get_request_args_v3(["target_id", "target_name", "target_ip"])

    target_id = args.get("target_id", "").strip()
    target_name = args.get("target_name", "").strip()

    if not target_id:
        return api_v3_response(status="fail", errcode=1040001, data={})

    if not target_name:
        return api_v3_response(status="fail", errcode=1040010, data={})

    if not NAME_RE.fullmatch(target_name):
        return api_v3_response(status="fail", errcode=1040019, data={})

    try:
        job_id = ObjectId(args.get("target_id"))
    except Exception:
        return api_v3_response(status="fail", errcode=1040001, data={})

    uid = g.audit_uid
    update = {"note": target_name}
    job, message = update_asset(uid, job_id, update)

    errcode = ERROR_CODE_MAP.get(message, 1050000)
    if not job:
        return api_v3_response(status="fail", errcode=errcode, message=message, data={})

    job.note = target_name
    results = asset_util.deal_job_info([job.to_mongo().to_dict()])
    return api_v3_response(message=message, data=results[0])


@api.route('/asset-del', methods=['POST'])
def asset_del():
    """资产删除"""
    args = get_request_args_v3(["target_id", ])
    try:
        del_data = BatchAssetJobId(**{"jobId": args.get("target_id")}).dict()
    except (ParameterError, ValidationError):
        return api_v3_response(status="fail", errcode=1040001, data={})

    uid = g.audit_uid
    job_ids = del_data["jobId"]
    if len(job_ids) > 50:
        return api_v3_response(status="fail", errcode=1040020, data={})

    jobs = Job.objects.filter(id__in=job_ids, uid=uid)
    exist_job_ids = [job.pk for job in jobs]

    count = del_asset(uid, job_ids, api=True)

    success_list = []
    fail_list = []
    for job_id in job_ids:
        if job_id not in exist_job_ids:
            fail_list.append({"target_id": str(job_id), "info": "资产不存在"})

    jobs = Job.objects.filter(id__in=job_ids, uid=uid)
    final_exist_job_ids = [job.pk for job in jobs]
    for job_id in exist_job_ids:
        if job_id in final_exist_job_ids:
            fail_list.append({"target_id": str(job_id), "info": "删除失败"})
        else:
            success_list.append({"target_id": str(job_id), "info": "删除成功"})

    return api_v3_response(message=f'成功删除资产{count}个', data={"fail": fail_list, "success": success_list})
